// A complete lexer and grammar for CSS 2.1 as defined by the
// W3 specification.
//
// This grammar is free to use providing you retain everyhting in this header comment
// section.
//
// Author      : Jim Idle, Temporal Wave LLC.
// Contact     : jimi@temporal-wave.com
// Website     : http://www.temporal-wave.com
// License     : ANTLR Free BSD License
//
// Please visit our Web site at http://www.temporal-wave.com and try our commercial
// parsers for SQL, C#, VB.Net and more.
//
// This grammar is free to use providing you retain everything in this header comment
// section.
//

//Modifications to the original css21 source file by Jim Idle have been done to fulfill the
//css3 parsing rules and making the parser more error prone.
//1) incorporated the grammar changes from selectors module: http://www.w3.org/TR/css3-selectors/#grammar
//      a. There's no 'universal' selector node, 'typeSelector' is used instead where instead of the identifier there's the star token.
//         This solves the too long (==3) lookahead problem in the simpleSelectorSequence rule
//2) implemented custom error recovery
//3) removed whitespaces from the alpha token fragments
//
//Please be aware that the grammar doesn't properly and fully reflect the whole css3 specification!!!

// The original CSS 2.1 grammar was downloaded from the antlr 3 grammars:
// https://raw.githubusercontent.com/antlr/grammars-v3/62555b5befd6f89af05bf918b79fa98f5391e12f/css21/css21.g

// Rebuild the lexer and parser:
// 1. Update Css3.g
// 2. Update the lexer/parser sources by running
//    ant generate-antlr-parser
//    from the module directory (ide/css.lib)
// 3. Rerun unittests
// 4. Commit Css3.g together with generated Css3Lexer.java and Css3Parser.java
//
// INFO: It is known, that the grammar does not compile without warnings
//

grammar Css3;

//options {
//	k='*';
//}

@header {
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// DO NOT EDIT THIS FILE MANUALLY!
// SEE Css3.g FOR INSTRUCTIONS

package org.netbeans.modules.css.lib;

}

@lexer::members {
    protected boolean isLessSource() {
        return false;
    }

    protected boolean isScssSource() {
        return false;
    }

    private boolean isCssPreprocessorSource() {
        return isLessSource() || isScssSource();
    }
}

@members {

    protected boolean isLessSource() {
        return false;
    }

    protected boolean isScssSource() {
        return false;
    }

    private boolean isCssPreprocessorSource() {
        return isLessSource() || isScssSource();
    }

    private boolean tokenNameEquals(String tokenImage) {
        return tokenImage.equalsIgnoreCase(input.LT(1).getText());
    }
    
    private boolean tokenNameEquals2(String tokenImage) {
        return tokenImage.equalsIgnoreCase(input.LT(2).getText());
    }

    private boolean tokenNameIs(String[] tokens) {
        for(String tokenImage : tokens) {
            if(tokenImage.equalsIgnoreCase(input.LT(1).getText())) {
                return true;
            }
        }
        return false;
    }

    private boolean tokenNameStartsWith(String prefix) {
        return input.LT(1).getText() != null
            && input.LT(1).getText().startsWith(prefix);
    }

    /**
     * Use the current stacked followset to work out the valid tokens that
     * can follow on from the current point in the parse, then recover by
     * eating tokens that are not a member of the follow set we compute.
     *
     * This method is used whenever we wish to force a sync, even though
     * the parser has not yet checked LA(1) for alt selection. This is useful
     * in situations where only a subset of tokens can begin a new construct
     * (such as the start of a new statement in a block) and we want to
     * proactively detect garbage so that the current rule does not exit on
     * on an exception.
     *
     * We could override recover() to make this the default behavior but that
     * is too much like using a sledge hammer to crack a nut. We want finer
     * grained control of the recovery and error mechanisms.
     */
    protected void syncToSet()
    {
        // Compute the followset that is in context wherever we are in the
        // rule chain/stack
        //
         BitSet follow = state.following[state._fsp]; //computeContextSensitiveRuleFOLLOW();

         syncToSet(follow);
    }

    protected void syncToSet(BitSet follow)
    {
        int mark = -1;

        //create error-recovery node
        dbg.enterRule(getGrammarFileName(), "recovery");

        try {

            mark = input.mark();

            // Consume all tokens in the stream until we find a member of the follow
            // set, which means the next production should be guaranteed to be happy.
            //
            while (! follow.member(input.LA(1)) ) {

                if  (input.LA(1) == Token.EOF) {

                    // Looks like we didn't find anything at all that can help us here
                    // so we need to rewind to where we were and let normal error handling
                    // bail out.
                    //
                    input.rewind();
                    mark = -1;
                    return;
                }
                input.consume();

                // Now here, because you are consuming some tokens, yu will probably want
                // to raise an error message such as "Spurious elements after the class member were discarded"
                // using whatever your override of displayRecognitionError() routine does to record
                // error messages. The exact error my depend on context etc.
                //
            }
        } catch (Exception e) {

          // Just ignore any errors here, we will just let the recognizer
          // try to resync as normal - something must be very screwed.
          //
        }
        finally {
            dbg.exitRule(getGrammarFileName(), "recovery");

            // Always release the mark we took
            //
            if  (mark != -1) {
                input.release(mark);
            }
        }
    }

        /**
         * synces to next RBRACE "}" taking nesting into account
         */
        protected void syncToRBRACE(int nest)
            {

                int mark = -1;
                //create error-recovery node
                //dbg.enterRule(getGrammarFileName(), "recovery");

                try {
                    mark = input.mark();
                    for(;;) {
                        //read char
                        int c = input.LA(1);

                        switch(c) {
                            case Token.EOF:
                                input.rewind();
                                mark = -1;
                                return ;
                            case Css3Lexer.LBRACE:
                                nest++;
                                break;
                            case Css3Lexer.RBRACE:
                                nest--;
                                if(nest == 0) {
                                    //do not eat the final RBRACE
                                    return ;
                                }
                        }

                        input.consume();

                    }

                } catch (Exception e) {

                  // Just ignore any errors here, we will just let the recognizer
                  // try to resync as normal - something must be very screwed.
                  //
                }
                finally {
                    if  (mark != -1) {
                        input.release(mark);
                    }
                    //dbg.exitRule(getGrammarFileName(), "recovery");
                }
            }

}

@lexer::header {
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.css.lib;
}

// -------------
// Main rule.   This is the main entry rule for the parser, the top level
//              grammar rule.
//
// A style sheet consists of an optional character set specification, an optional series
// of imports, and then the main body of style rules.
//
styleSheet
    :
    	ws?
    	( charSet ws? )?
        ( layerStatement ws? )?
        imports?
        namespaces?
        body?
     EOF
    ;

namespaces
	:
	( namespace ws? )+
	;

namespace
  : NAMESPACE_SYM ws? (namespacePrefixName ws?)? resourceIdentifier ws? SEMI
  ;

namespacePrefixName
  : IDENT
  ;

resourceIdentifier
  : STRING | URI
  ;

charSet
    :   CHARSET_SYM ws? charSetValue ws? SEMI
    ;

charSetValue
	: STRING
	;

imports
	:
	(
            ( importItem ws? SEMI ws? )
            |
            {isScssSource()}? ( sass_use ws? SEMI ws? )
            |
            {isScssSource()}? ( sass_forward ws? SEMI ws? )
        )+
	;

importItem
    :
        IMPORT_SYM ws? resourceIdentifier ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
        |
        //multiple imports in one directive
        {isScssSource()}? IMPORT_SYM ws? resourceIdentifier (ws? COMMA ws? resourceIdentifier)* ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
        |
        {isLessSource()}? IMPORT_SYM ws? (LPAREN less_import_types RPAREN ws?)? resourceIdentifier ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
    ;

importLayer
    :
    {tokenNameEquals("layer")}? IDENT (LPAREN ws? layerName ws? RPAREN)?
    ;

sass_use
    :
        SASS_USE ws resourceIdentifier (ws sass_use_as)? (ws sass_use_with)?
    ;

sass_use_as
    :
    {tokenNameEquals("as")}? IDENT ws IDENT
    ;

sass_use_with
    :
    {tokenNameEquals("with")}? IDENT ws? LPAREN ws? sass_use_with_declaration  (ws? COMMA ws? sass_use_with_declaration)*  ws? RPAREN
    ;

sass_use_with_declaration
    :
    cp_variable ws? COLON ws? cp_expression
    ;


sass_forward
    :
        SASS_FORWARD ws resourceIdentifier ( ws ( sass_forward_hide |  sass_forward_show))? ({tokenNameEquals2("as")}? ws sass_forward_as)? ({tokenNameEquals2("with")}? ws sass_forward_with)?
    ;

sass_forward_as
    :
    {tokenNameEquals("as")}? IDENT ws IDENT
    ;

sass_forward_with
    :
    {tokenNameEquals("with")}? IDENT ws? LPAREN ws? sass_forward_with_declaration  (ws? COMMA ws? sass_forward_with_declaration)*  ws? RPAREN
    ;

sass_forward_with_declaration
    :
    cp_variable ws? COLON ws? cp_expression
    ;

sass_forward_hide
    :
    {tokenNameEquals("hide")}? IDENT ws IDENT (ws? COMMA ws? IDENT)*
    ;

sass_forward_show
    :
    {tokenNameEquals("show")}? IDENT ws IDENT (ws? COMMA ws? IDENT)*
    ;

media
    : MEDIA_SYM ws?
    (
         mediaQueryList
    ) ws?
    LBRACE ws? syncToFollow
        mediaBody?
    RBRACE
    ;

mediaBody
    :
    (
         ( mediaBodyItem ((ws? SEMI)=>ws? SEMI)? ws? )
         |
         ( SEMI ws? )
         | ({isScssSource()}? sass_extend (ws | (SEMI)))
    )+
    ;

mediaBodyItem
    :
    (SASS_MIXIN | (((DOT IDENT) | HASH) ws? LPAREN (~RPAREN)* RPAREN ~(LBRACE|SEMI)* LBRACE))=>cp_mixin_declaration
    //https://netbeans.org/bugzilla/show_bug.cgi?id=227510#c12 -- class selector in selector group recognized as mixin call -- workarounded by adding the ws? SEMI to the predicate
    | (cp_mixin_call (ws? IMPORTANT_SYM)? ws? SEMI)=> {isLessSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?
    | (cp_mixin_call)=> {isScssSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?
    | (((SASS_AT_ROOT (ws selectorsGroup)? ) | (SASS_AT_ROOT ws LPAREN ws? IDENT ws? COLON ws? IDENT ws? RPAREN) | selectorsGroup) ws? LBRACE)=>rule
    | (propertyDeclaration)=>propertyDeclaration
    | {isScssSource()}? sass_debug
    | {isScssSource()}? sass_control
    | {isScssSource()}? sass_content
    | {isCssPreprocessorSource()}? importItem
    | rule
    | page
    | fontFace
    | vendorAtRule
    //Just a partial hotfix for nested MQ: complete grammar is defined in: http://www.w3.org/TR/css3-conditional/#processing
    | media
    | supportsAtRule
    ;

mediaQueryList
 : mediaQuery ( (ws? COMMA)=> ws? COMMA ws? mediaQuery )*
 ;

mediaQuery
 :
    (mediaQueryOperator ws? )?  mediaType ((ws? key_and)=> ws? key_and ws? mediaExpression )*
    | mediaExpression ((ws? key_and)=> ws? key_and ws? mediaExpression )*
    | {isLessSource()}? cp_variable
 ;

mediaQueryOperator
 	: key_only | NOT
 	;

mediaType
 : IDENT | GEN | {isCssPreprocessorSource()}? sass_interpolation_expression_var
 ;

mediaExpression
    :
    (LPAREN) => (LPAREN ws? mediaFeature mediaFeatureValue? ws? RPAREN)
    | (HASH) => {isCssPreprocessorSource()}? sass_interpolation_expression_var
    ;

mediaFeatureValue
    :
    ws? COLON ws?
    (
        {isCssPreprocessorSource()}? cp_expression
        |
        expression
    )
    ;

mediaFeature
 : IDENT | GEN | {isCssPreprocessorSource()}? ( cp_variable | sass_interpolation_expression_var )
 ;

 body
    :
    (
         ( bodyItem ((ws? SEMI)=>ws? SEMI)? ws? )
         |
         ( SEMI ws? )
    )+
    ;

bodyItem
    :
        (SASS_MIXIN | (((DOT IDENT) | HASH) ws? LPAREN (~RPAREN)* RPAREN ~(LBRACE|RBRACE|SEMI)* LBRACE))=>cp_mixin_declaration
        //https://netbeans.org/bugzilla/show_bug.cgi?id=227510#c12 -- class selector in selector group recognized as mixin call -- workarounded by adding the ws? SEMI to the predicate
        | (cp_mixin_call ws? SEMI)=> {isLessSource()}? cp_mixin_call
        | (cp_mixin_call)=> {isScssSource()}? cp_mixin_call
    	| rule
        | (cp_variable ws? COLON)=> cp_variable_declaration
        | (sass_map)=> sass_map
        | at_rule
        //not exactly acc. to the spec, since just CP stuff can preceede, but is IMO satisfactory
        | {isCssPreprocessorSource()}? importItem
        | {isScssSource()}? sass_debug
        | {isScssSource()}? sass_control
        | {isScssSource()}? sass_function_declaration
    ; catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(NL));
    }

supportsAtRule
	:
	SUPPORTS_SYM ws? supportsCondition ws? LBRACE ws? syncToFollow mediaBody? RBRACE
	;


supportsCondition
	:
	NOT ws supportsInParens
	| supportsInParens (ws supportsWithOperator)?
	;

supportsWithOperator
        :
        supportsConjunction (ws supportsConjunction)*
        | supportsDisjunction (ws supportsDisjunction)*
        ;

supportsConjunction
        : (key_and ws supportsInParens)
        ;

supportsDisjunction
        : (key_or ws supportsInParens)
        ;

supportsInParens options {backtrack=true;}
	:
	LPAREN ws? supportsCondition ws? RPAREN
	| supportsFeature
        | function
        // this is still lacking ( <any-value>?) - lets see whether this becomes
        // a problem or not
	;

supportsFeature
	:
	supportsDecl
	;

supportsDecl
	:
	LPAREN ws? declaration ws? RPAREN
	;

containerAtRule options {backtrack=true;}
	:
	(CONTAINER_SYM ws containerCondition ws? LBRACE) => CONTAINER_SYM ws containerCondition ws? LBRACE ws? syncToFollow body? RBRACE
	| CONTAINER_SYM ws containerName ws containerCondition ws? LBRACE ws? syncToFollow body? RBRACE
	;

containerCondition
        :
        NOT ws containerQueryInParens
        | containerQueryInParens (ws containerQueryWithOperator)?
        ;

containerQueryWithOperator
        :
        containerQueryConjunction (ws containerQueryConjunction)*
        | containerQueryDisjunction (ws containerQueryDisjunction)*
        ;

containerQueryConjunction
        : (key_and ws containerQueryInParens)
        ;

containerQueryDisjunction
        : (key_or ws containerQueryInParens)
        ;

containerQueryInParens options {backtrack=true;}
	:
	LPAREN ws? containerCondition ws? RPAREN
	| sizeFeature
	| {tokenNameEquals("style")}? IDENT ws? LPAREN ws? styleQuery ws? RPAREN
        | function
        // this is still lacking ( <any-value>?) - lets see whether this becomes
        // a problem or not
	;

containerName
        : IDENT
        ;

styleQuery:
        styleCondition
        | styleFeature
        ;

styleCondition:
        NOT ws styleInParens
        | styleInParens (ws styleConditionWithOperator)
        ;

styleConditionWithOperator
        :
        styleQueryConjunction (ws styleQueryConjunction)*
        | styleQueryDisjunction (ws styleQueryDisjunction)*
        ;

styleQueryConjunction
        : (key_and ws styleInParens)
        ;

styleQueryDisjunction
        : (key_or ws styleInParens)
        ;

styleInParens options {backtrack=true;}
        :
        LPAREN ws? styleCondition ws? RPAREN
        | LPAREN ws? styleFeature ws? RPAREN
        | function
        // this is still lacking ( <any-value>?) - lets see whether this becomes
        // a problem or not
        ;

sizeFeature options {backtrack=true;}
        :
        LPAREN ws? sizeFeatureFixedValue ws? RPAREN
        | LPAREN ws? sizeFeatureRangeSingle ws? RPAREN
        | LPAREN ws? sizeFeatureRangeBetweenLt ws? RPAREN
        | LPAREN ws? sizeFeatureRangeBetweenGt ws? RPAREN
        ;

sizeFeatureFixedValue
        :
        sizeFeatureName ( ws? COLON ws? sizeFeatureValue)?
        ;

sizeFeatureRangeSingle
        :
        (sizeFeatureName | sizeFeatureValue) ws? (OPEQ | LESS | LESS_OR_EQ | GREATER | GREATER_OR_EQ) ws? (sizeFeatureName | sizeFeatureValue)
        ;

sizeFeatureRangeBetweenLt
        :
        sizeFeatureValue ws? (LESS | LESS_OR_EQ) ws? sizeFeatureName ws? (LESS | LESS_OR_EQ) ws? sizeFeatureValue
        ;

sizeFeatureRangeBetweenGt
        :
        sizeFeatureValue ws? (GREATER | GREATER_OR_EQ) ws? sizeFeatureName ws? (GREATER | GREATER_OR_EQ) ws? sizeFeatureValue
        ;

sizeFeatureName
        :
        IDENT
        | VARIABLE
        ;

sizeFeatureValue
        :
        term
        ;

styleFeature
        :
        declaration
        ;

layerAtRule
        :
        layerBlock
        |
        layerStatement
        ;

layerBlock
        :
        (LAYER_SYM ws layerName? ws? layerBody)
        ;

layerStatement
        :
        (LAYER_SYM ws layerName ( ws? COMMA ws? layerName)* SEMI)
        ;

layerName
        :
        IDENT (DOT IDENT)*
        ;

layerBody
        :
        LBRACE ws? body? ws? RBRACE
        ;

at_rule
    :
    media
    | page
    | counterStyle
    | fontFace
    | supportsAtRule
    | vendorAtRule
    | layerAtRule
    | containerAtRule
    ;

vendorAtRule
: moz_document | webkitKeyframes | generic_at_rule;

atRuleId
	:
	IDENT | STRING | {isCssPreprocessorSource()}? ( cp_variable | sass_interpolation_expression_var )
	;

generic_at_rule
    : AT_IDENT ws? ( atRuleId ws? )?
        LBRACE
        	syncTo_RBRACE
        RBRACE
	;
moz_document
	:
	MOZ_DOCUMENT_SYM ws? ( moz_document_function ws?) ( COMMA ws? moz_document_function ws? )*
	LBRACE ws?
		body? //can be empty
	RBRACE
	;

moz_document_function
	:
	URI | MOZ_URL_PREFIX | MOZ_DOMAIN | MOZ_REGEXP
	;

//http://developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariCSSRef/Articles/OtherStandardCSS3Features.html#//apple_ref/doc/uid/TP40007601-SW1
webkitKeyframes
	:
	WEBKIT_KEYFRAMES_SYM ws? atRuleId ws?
	LBRACE ws?
		( webkitKeyframesBlock ws? )*
	RBRACE
	;

webkitKeyframesBlock
	:
	webkitKeyframeSelectors ws?
	LBRACE  ws? syncToFollow
		declarations?
	RBRACE
        | {isScssSource()}?  {isScssSource()}? sass_content SEMI?
	;

webkitKeyframeSelectors
	:
	( {tokenNameEquals("from")}? IDENT | {tokenNameEquals("to")}? IDENT | PERCENTAGE ) ( ws? COMMA ws? ( {tokenNameEquals("from")}? IDENT | {tokenNameEquals("to")}? IDENT | PERCENTAGE ) )*
	;

page
@init {
    boolean semiRequired = false;
}
    : PAGE_SYM ws? ( IDENT ws? )? (pseudoPage ws?)?
        LBRACE
            //the grammar in the http://www.w3.org/TR/css3-page/ says the declaration/margins should be delimited by the semicolon,
            //but there's no such char in the examples => making it arbitrary
            ( ws? ({semiRequired}? (SEMI ws?) | (SEMI ws?)?) (propertyDeclaration{semiRequired=true;}|margin{semiRequired=false;}))*
            SEMI?
            ws?
        RBRACE
    ;

counterStyle
    : COUNTER_STYLE_SYM ws? IDENT ws?
        LBRACE ws? syncToDeclarationsRule
		declarations?
        RBRACE
    ;

fontFace
    : FONT_FACE_SYM ws?
        LBRACE ws? syncToDeclarationsRule
		declarations?
        RBRACE
    ;

margin
	: margin_sym ws? LBRACE ws? syncToDeclarationsRule declarations? RBRACE
       ;

margin_sym
	:
       TOPLEFTCORNER_SYM |
       TOPLEFT_SYM |
       TOPCENTER_SYM |
       TOPRIGHT_SYM |
       TOPRIGHTCORNER_SYM |
       BOTTOMLEFTCORNER_SYM |
       BOTTOMLEFT_SYM |
       BOTTOMCENTER_SYM |
       BOTTOMRIGHT_SYM |
       BOTTOMRIGHTCORNER_SYM |
       LEFTTOP_SYM |
       LEFTMIDDLE_SYM |
       LEFTBOTTOM_SYM |
       RIGHTTOP_SYM |
       RIGHTMIDDLE_SYM |
       RIGHTBOTTOM_SYM
       ;

pseudoPage
    : COLON IDENT
    ;

operator
    : SOLIDUS
    | COMMA
    ;

unaryOperator
    : MINUS
    | PLUS
    ;

property
    :

    //parse as scss_declaration_interpolation_expression only if it really contains some #{} content
    //(the IE allows also just ident as its content)
    {isScssSource()}? sass_selector_interpolation_exp
    | {isLessSource()}? less_selector_interpolation_exp
    | VARIABLE
    | IDENT
    | GEN
    | {isCssPreprocessorSource()}? cp_variable

    ; catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(COLON));
    }

sass_map
    :
    sass_map_name COLON ws? LPAREN ws? syncToFollow
        //what can be in the map? --- just properties? 
        sass_map_pairs?
    RPAREN ((ws? SASS_DEFAULT) | (ws? SASS_GLOBAL))*
    ;

sass_map_name
    :
    cp_variable
    ;

sass_map_pairs
    :
    (
         ( sass_map_pair ((ws? COMMA)=>ws? COMMA)? ws? )
         |
         ( COMMA ws? )
    )+
    ;

sass_map_pair
    :
        (NUMBER|(STRING (ws? STRING)*)|((function)=>function)|property|sass_map) ws? COLON ws? cp_expression (ws? prio)?
    ;

rule
    :
        (
            (SASS_AT_ROOT (ws selectorsGroup)?) 
            | (SASS_AT_ROOT ws LPAREN ws? {tokenNameEquals("without") || tokenNameEquals("with")}? IDENT /* with || without */ ws? COLON ws? IDENT ws? RPAREN) 
            | selectorsGroup
        ) ws?
    LBRACE ws? syncToFollow
        declarations?
    RBRACE
    ;
    catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(RBRACE));
        input.consume(); //consume the RBRACE as well
    }

declarations
    :
       (SEMI ws?)*  declaration (((ws? (SEMI ws?)+)|ws) declaration)* ((ws? (SEMI ws?)+)|ws)?
    |  (SEMI ws?)+ 
    ;

declaration
    :
    (cp_variable_declaration)=>cp_variable_declaration
    | (sass_map)=> sass_map
    | (sass_nested_properties)=>sass_nested_properties
    | (((SASS_AT_ROOT (ws selectorsGroup)? ) | (SASS_AT_ROOT ws LPAREN ws? IDENT ws? COLON ws? IDENT ws? RPAREN) | selectorsGroup) ws? LBRACE)=>rule
    | (propertyDeclaration)=>propertyDeclaration
    //for the error recovery - if the previous synt. predicate fails (an error in the declaration we'll still able to recover INSIDE the declaration
    | (property ws? COLON ~(LBRACE|SEMI|RBRACE)* (RBRACE|SEMI) )=>propertyDeclaration
    | (cp_mixin_declaration)=>cp_mixin_declaration
    | (cp_mixin_call)=> cp_mixin_call (ws? IMPORTANT_SYM)?
    | (cp_mixin_call)=> {isScssSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?    
    | {isCssPreprocessorSource()}? at_rule
    | {isScssSource()}? sass_control
    | {isScssSource()}? sass_extend
    | {isScssSource()}? sass_debug
    | {isScssSource()}? sass_content
    | {isScssSource()}? sass_function_return
    | {isScssSource()}? sass_error
    | {isScssSource()}? importItem
    | GEN
    ;
    catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(SEMI));
    }

selectorsGroup
    :
        selector (ws? COMMA ws? selector)* ({isCssPreprocessorSource()}? COMMA)?
    ;

selector
    :  (combinator ws?)? simpleSelectorSequence ( ((ws? combinator ws?)|ws) simpleSelectorSequence)*
       | {isScssSource()}? combinator
    ;

combinator
    :
    PLUS | GREATER | TILDE
    ;

simpleSelectorSequence
	:
        (elementSubsequent | {isScssSource()}? sass_selector_interpolation_exp
        | {isLessSource()}? less_selector_interpolation_exp  ) ((ws? esPred)=>((ws? elementSubsequent) |(ws ({isScssSource()}? sass_selector_interpolation_exp | {isLessSource()}? less_selector_interpolation_exp))))*
	| (typeSelector)=>typeSelector ((ws? esPred)=>((ws? elementSubsequent) | {isScssSource()}? ws sass_selector_interpolation_exp))* 
	;
	catch[ RecognitionException rce] {
            reportError(rce);
            consumeUntil(input, BitSet.of(LBRACE));
        }

//predicate
esPred
    : HASH_SYMBOL | HASH | DOT | LBRACKET | COLON | DCOLON | SASS_EXTEND_ONLY_SELECTOR | {isCssPreprocessorSource()}? LESS_AND
    ;

typeSelector
	options { k = 2; }
 	:  (((IDENT | STAR)? PIPE)=>namespacePrefix)? elementName
 	;

namespacePrefix
  : ( namespacePrefixName | STAR)? PIPE
  ;


elementSubsequent
    :
    (
        {isScssSource()}? sass_extend_only_selector
        | {isCssPreprocessorSource()}? LESS_AND (IDENT | NUMBER | {isScssSource()}? sass_interpolation_expression_var)*
        | {isLessSource()}? LESS_AND less_selector_interpolation_exp
    	| cssId
    	| cssClass
        | slAttribute
        | pseudo
    )
    ;

//Error Recovery: Allow the parser to enter the cssId rule even if there's just hash char.
cssId
    : HASH ({isScssSource()}? sass_selector_interpolation_exp)?
      |
        ( HASH_SYMBOL
            ( NAME
              | {isLessSource()}? less_selector_interpolation_exp // #@{var} { ... }
            )
        )
    ;
    catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(WS, IDENT, LBRACE));
    }

cssClass
    : (DOT
        (
             {isScssSource()}?  sass_selector_interpolation_exp
            | {isLessSource()}? less_selector_interpolation_exp
            | IDENT
            | NOT
            | GEN
        )
      ) | {tokenNameStartsWith(".")}? DIMENSION
    ;
    catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(WS, IDENT, LBRACE));
    }

//using typeSelector even for the universal selector since the lookahead would have to be 3 (IDENT PIPE (IDENT|STAR) :-(
elementName
    : IDENT | GEN | LESS_AND | STAR
    ;

slAttribute
    : LBRACKET
    	namespacePrefix? ws?
        slAttributeName ws?

            (
                (
                      OPEQ
                    | INCLUDES
                    | DASHMATCH
                    | BEGINS
                    | ENDS
                    | CONTAINS
                )
                ws?
                slAttributeValue
                ws?
            )?

      RBRACKET
;
catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(IDENT, LBRACE));
    }

//bit similar naming to attrvalue, attrname - but these are different - for functions
slAttributeName
	: IDENT
	;

slAttributeValue
	:
	(
  	      IDENT
              | STRING
        )
        ;

pseudo
    : ( COLON | DCOLON )
             (
                (
                    ( IDENT | GEN )
                    ( // Function
                        ws? LPAREN ws? ( (expression ws?) | STAR )? RPAREN
                    )?
                )
                | {isScssSource()}? sass_interpolation_expression_var
                | ( NOT ws? LPAREN ws? ( selectorsGroup ws?)? RPAREN )
                | {tokenNameEquals("is") || tokenNameEquals("where") || tokenNameEquals("has")}? ( IDENT ws? LPAREN ws? ( selectorsGroup ws?)? RPAREN )
                | ({isLessSource()}? {tokenNameEquals("extend")}? IDENT ws? LPAREN ws? selectorsGroup? RPAREN)
             ) 
    ;

propertyDeclaration
    :
      {isCssPreprocessorSource() && !tokenNameStartsWith("--")}? STAR? property ws? COLON ws? cp_propertyValue //cp_expression may contain the IMPORT_SYM
    | {tokenNameStartsWith("--")}? property ws? COLON ws? componentValueOuter?
    | STAR? property ws? COLON ws? propertyValue (ws? prio)?
    
    ;
    catch[ RecognitionException rce] {
        reportError(rce);
        //recovery: if an mismatched token occures inside a declaration is found,
        //then skip all tokens until an end of the rule is found represented by right curly brace
        consumeUntil(input, BitSet.of(SEMI, RBRACE));
    }

//XXX this is a hack for the IMPORT_SYM inside cp_expression
cp_propertyValue
    :
    {isCssPreprocessorSource()}? cp_expression_list
    | propertyValue
    ;

propertyValue
	:
        expression
	;

//an expression wich doesn't contain cp expression operators
expressionPredicate
    options { k = 1; }
    :
    ( ~ (AT_IDENT | STAR | SOLIDUS | LBRACE | SEMI | RBRACE | SASS_VAR) )+ ( SEMI | RBRACE )
    ;

preservedToken: ~ (LPAREN | LBRACE | LBRACKET | RPAREN | RBRACE | RBRACKET);

preservedTokenTopLevel: ~ (LPAREN | LBRACE | LBRACKET | RPAREN | RBRACE | RBRACKET | SEMI );

braceBlock: LBRACE componentValue+ RBRACE;

bracketBlock: LBRACKET componentValue+ RBRACKET;

parenBlock: LPAREN componentValue+ RPAREN;

componentValue: parenBlock | braceBlock | bracketBlock | (functionName ws? LPAREN) => function | preservedToken;

componentValueOuter: (parenBlock | braceBlock | bracketBlock | (functionName ws? LPAREN) => function | preservedTokenTopLevel) componentValueOuter*;

//recovery: syncs the parser to the first identifier in the token input stream or the closing curly bracket
//since the rule matches epsilon it will always be entered
syncToDeclarationsRule
    @init {
        //why sync to DOT? - LESS allows class rules nested
        syncToSet(BitSet.of(IDENT, RBRACE, STAR, DOT));
    }
    	:
    	;

syncTo_RBRACE
    @init {
        syncToRBRACE(1); //initial nest == 1
    }
    	:
    	;

syncTo_SEMI
    @init {
        syncToSet(BitSet.of(SEMI));
    }
    	:
            SEMI
    	;

//synct to computed follow set in the rule
syncToFollow
    @init {
        syncToSet();
    }
    	:
    	;

prio
    : IMPORTANT_SYM
    ;

expression
    : term ( (( ws | (ws? operator ws?) | /* nothing */) term)=> ( ws | (ws? operator ws?) | /* nothing */) term)*
    ;

term
    :
    ( unaryOperator ws? )?
    (
        (functionName ws? LPAREN)=>function //"myfunction(" as predicate
        | VARIABLE
        | {! (isScssSource() && tokenNameEquals2("."))}? IDENT
        | (LBRACKET WS? IDENT (WS IDENT)* WS? RBRACKET)
        | NUMBER
        | URANGE
        | PERCENTAGE
        | LENGTH
        | EMS
        | REM
        | EXS
        | ANGLE
        | TIME
        | FREQ
        | RESOLUTION
        | DIMENSION     //so we can match expression like a:nth-child(3n+1) -- the "3n" is lexed as dimension
        | STRING
        | TILDE ( STRING | LESS_JS_STRING ) //less escaped string/js string
        | LESS_JS_STRING   //less js string
        | GEN
        | URI
        | hexColor
        | {isCssPreprocessorSource()}? cp_variable
        | {isScssSource()}? LESS_AND
        | {isScssSource()}? sass_interpolation_expression_var
        | {isLessSource()}? less_selector_interpolation
        | {isCssPreprocessorSource()}? cp_term_symbol //accept any garbage in preprocessors
    )
    ;

//SASS/LESS expressions workaround
//Bug 233359 - false error in SASS editor
//https://netbeans.org/bugzilla/show_bug.cgi?id=233359
cp_term_symbol
    : PERCENTAGE_SYMBOL //what else?
    ;

function
	: 	functionName
		LPAREN ws?
		(
                    fnAttributes
                    | //empty
		)
		RPAREN
	;
catch[ RecognitionException rce] {
        reportError(rce);
        consumeUntil(input, BitSet.of(RPAREN, SEMI, RBRACE));
}

functionName
        //css spec allows? here just IDENT,
        //but due to some nonstandart MS extension like progid:DXImageTransform.Microsoft.gradien
        //the function name can be a bit more complicated
	:
        (IDENT COLON)? IDENT (DOT IDENT)*
    	;

fnAttributes
    :
    fnAttribute (ws? (COMMA | {isLessSource()}? SEMI) ws? fnAttribute)* ws?
    ;

fnAttribute
	:
        (fnAttributeName ws? (OPEQ|COLON) )=>fnAttributeName ws? (OPEQ|COLON) ws? fnAttributeValue
        | (cp_expression)=> cp_expression
        | expression
	;

fnAttributeName
	:
            IDENT (DOT IDENT)*
            | {isCssPreprocessorSource()}? cp_variable
	;

fnAttributeValue
	:
            term ( (( ws | (ws? SOLIDUS ws?) | /* nothing */) term)=> ( ws | (ws? SOLIDUS ws?) | /* nothing */) term)* //== expression w/o COMMAs
            | {isCssPreprocessorSource()}? cp_math_expression
	;

hexColor
    : HASH
    ;

ws
    : ( WS | NL | COMMENT )+
    ;

//*** LESS SYNTAX ***
//Some additional modifications to the standard syntax rules has also been done.
//ENTRY POINT FROM CSS GRAMMAR
cp_variable_declaration
    :
        {isLessSource()}? cp_variable ws? COLON ws? cp_expression_list
        |
        {isScssSource()}? cp_variable ws? COLON ws? cp_expression_list ((ws? SASS_DEFAULT) | (ws? SASS_GLOBAL))*
    ;

//ENTRY POINT FROM CSS GRAMMAR
cp_variable
    :
        //every token which might possibly begin with the at sign
        {isLessSource()}? ( AT_IDENT | IMPORT_SYM | PAGE_SYM | MEDIA_SYM | NAMESPACE_SYM | CHARSET_SYM | COUNTER_STYLE_SYM | FONT_FACE_SYM | TOPLEFTCORNER_SYM | TOPLEFT_SYM | TOPCENTER_SYM | TOPRIGHT_SYM | TOPRIGHTCORNER_SYM | BOTTOMLEFTCORNER_SYM | BOTTOMLEFT_SYM | BOTTOMCENTER_SYM | BOTTOMRIGHT_SYM | BOTTOMRIGHTCORNER_SYM | LEFTTOP_SYM | LEFTMIDDLE_SYM | LEFTBOTTOM_SYM | RIGHTTOP_SYM | RIGHTMIDDLE_SYM | RIGHTBOTTOM_SYM | MOZ_DOCUMENT_SYM | WEBKIT_KEYFRAMES_SYM | SASS_CONTENT | SASS_MIXIN | SASS_INCLUDE | SASS_EXTEND | SASS_DEBUG | SASS_WARN | SASS_IF | SASS_ELSE | SASS_FOR | SASS_FUNCTION | SASS_RETURN | SASS_EACH | SASS_WHILE | SASS_AT_ROOT | SASS_USE | SASS_FORWARD )
        |
        {isScssSource()}? ( SASS_VAR | IDENT DOT SASS_VAR )
    ;

//comma separated list of cp_expression-s
cp_expression_list
    :
    (cp_expression) => cp_expression
    ((ws? COMMA ws? cp_expression)=>ws? COMMA ws? cp_expression)*
    ;

//expression:
//-----------
//
//allowed content:
//- boolean expression binary operators: and, or, <, >, <=, ==, ...
//- boolean expression unary operator: not
//- mathematical expression as term: cp_math_expression
//- whitespace separated list of expression-s
//- comma separted list of expressions-s in parenthesis
//
cp_expression
    :
    {isLessSource()}? (LBRACE ws? syncToFollow declarations? RBRACE)
    | (cp_expression_atom) => (cp_expression_atom
    (
        (ws? cp_expression_operator)=>(ws? cp_expression_operator ws?) cp_expression_atom
        | (ws? cp_expression_atom)=>ws? cp_expression_atom
    )*)
    | {isScssSource()}? LPAREN ws? syncToFollow sass_map_pairs? RPAREN
    ;

cp_expression_operator
    :
    key_or | key_and  | CP_EQ | CP_NOT_EQ | LESS | LESS_OR_EQ | GREATER | GREATER_OR_EQ
    ;

cp_expression_atom
    :
        (NOT ws?)?
        (
            (cp_math_expression)=>cp_math_expression
            | LPAREN ws? (cp_expression_list ws?)? RPAREN
        )
    ;

//WS separated list of cp_math_expression-s
cp_math_expressions
    :
    cp_math_expression
    (ws cp_math_expression)*
    ;
//mathematical expression:
//-------------------------
//allowed content:
//- parens: ()
//- binary oparators: +,-,*
//- unary operators: +,-
//- terms
//- SASS interpolation expression where the term is allowed
//
//NOT ALLOWED:
//- COMMAS
//- terms separated just by whitespace - e.g. "one two"
//
cp_math_expression
    :    cp_math_expression_atom
         (
            (ws? (PLUS|MINUS|STAR|SOLIDUS) )=> ws? (PLUS|MINUS|STAR|SOLIDUS) ws? cp_math_expression_atom
         )*
    ;

cp_math_expression_atom
    :
    term
    | IMPORTANT_SYM //cp property value may contain any gargabe - TODO - possibly add other garbage tokens
    | ( unaryOperator ws? )? LPAREN ws? cp_math_expression ws? RPAREN
    ;

//parametric mixins:
//    .border-radius (@radius)
//    .box-shadow (@x: 0, @y: 0, @blur: 1px, @color: #000)
//
//normal mixin has common css syntax: .mixin so cannot be distinguished from a css class
//ENTRY POINT FROM CSS GRAMMAR
cp_mixin_declaration
    :
    (
        {isLessSource()}? (LESS_AND | (((DOT cp_mixin_name) | HASH) ws? LPAREN ws? cp_args_list? RPAREN)) (ws? less_mixin_guarded)?
        |
        {isScssSource()}? SASS_MIXIN ws cp_mixin_name (ws? LPAREN ws? cp_args_list? RPAREN)?
    )
    ws? cp_mixin_block
    ;

//allow: .mixin; .mixin(); .mixin(@param, #77aa00);
//ENTRY POINT FROM CSS GRAMMAR
cp_mixin_call
    :
    (
        {isLessSource()}? (DOT cp_mixin_name | HASH | AT_IDENT | LESS_AND) ((ws? combinator ws?) => ws? combinator ws? (DOT cp_mixin_name | HASH | AT_IDENT | LESS_AND))* ((pseudo)=>pseudo | (ws? LPAREN)=>(ws? LPAREN ws? cp_mixin_call_args? RPAREN))?
        |
        {isScssSource()}? SASS_INCLUDE ws cp_mixin_name (ws? LPAREN ws? cp_mixin_call_args? RPAREN)? (ws? cp_mixin_block)?
    )
    ;

cp_mixin_block
    :
    LBRACE ws? syncToFollow
        (declarations | (webkitKeyframeSelectors) => 
		( webkitKeyframesBlock ws? )*)?
    RBRACE
    ;

cp_mixin_name
    :
    IDENT
    ;

cp_mixin_call_args
    :
    //the term separatos is supposed to be just COMMA, but in some weird old? samples
    //I found semicolon used as a delimiter between arguments
    cp_mixin_call_arg ( (COMMA | SEMI) ws? cp_mixin_call_arg)*  (CP_DOTS ws?)? SEMI?
    ;

cp_mixin_call_arg
    :
    (
        cp_variable ws? COLON ws? cp_expression
        | cp_expression
    ) ws?
    ;

//.box-shadow ("@x: 0, @y: 0, @blur: 1px, @color: #000")
cp_args_list
    :
    //the term separatos is supposed to be just COMMA, but in some weird old? samples
    //I found semicolon used as a delimiter between arguments

    //sass varargs:
    //@mixin box-shadow($shadows...) {} -- note that now also LESS parser allows this incorrectly (minor issue)

    ( cp_arg ( ( COMMA | SEMI ) ws? cp_arg)*  ( (COMMA | SEMI) ws? )? ( (CP_DOTS | LESS_REST) ws? )?)
    |
    (CP_DOTS | LESS_REST) ws?
    ;

//.box-shadow ("@x: 0", @y: 0, @blur: 1px, @color: #000)
cp_arg
    :
    cp_variable ws? ( COLON ws? cp_expression ws?)?
    | {isLessSource()}? IDENT
    ;

//.mixin (@a) "when (lightness(@a) >= 50%)" {
//.mixin (@a) "when (@a > 10), (@a < -10)" { ... }
less_mixin_guarded
    :
    less_when ws? less_condition (ws? (COMMA | key_and) ws? less_condition)*
    ;

//.truth (@a) when (@a) { ... }
//.truth (@a) when (@a = true) { ... }
less_condition
    :
    (NOT ws?)?
    LPAREN ws?
        (
             (cp_variable | less_function_in_condition) ws? (less_condition_operator ws? cp_math_expression)?
        )
    RPAREN
    ;

//.mixin (@a, @b: 0) when ("isnumber(@b)") { ... }
less_function_in_condition
    :
    less_fn_name ws? LPAREN ws? cp_variable ws? RPAREN
    ;

//.mixin (@a, @b: 0) when ("isnumber"(@b)) { ... }
less_fn_name
    :
    IDENT
    ;

less_condition_operator
    :
    GREATER | GREATER_OR_EQ | OPEQ | LESS | LESS_OR_EQ
    ;

less_selector_interpolation_exp :
    (IDENT | MINUS)? less_selector_interpolation (less_selector_interpolation_exp | ( IDENT | MINUS | DIMENSION | LENGTH)+)?
    ;

less_selector_interpolation
    :
    AT_SIGN LBRACE ws? IDENT ws? RBRACE
    ;

//SCSS interpolation expression
sass_selector_interpolation_exp :
    (IDENT | MINUS)? sass_interpolation_expression_var (sass_selector_interpolation_exp | ( IDENT | MINUS | DIMENSION | LENGTH)+)?
    ;

sass_interpolation_expression_var
    :
        HASH_SYMBOL LBRACE WS? cp_expression WS? RBRACE //XXX possibly allow cp_expression inside
    ;

//SASS nested properties:
//.funky {
//  font: 2px/3px {
//    family: fantasy;
//    size: 30em;
//    weight: bold;
//  }
//}
//
//or just:
//
//.funky {
//  font: {
//    family: fantasy;
//    size: 30em;
//    weight: bold;
//  }
//}
sass_nested_properties
    :
    property ws? COLON ws? (propertyValue ws?)? LBRACE ws? syncToFollow declarations? RBRACE
    ;

sass_extend
    :
    SASS_EXTEND ws simpleSelectorSequence (ws? COMMA ws? simpleSelectorSequence)* (ws SASS_OPTIONAL)?
    ;

sass_extend_only_selector
    :
    SASS_EXTEND_ONLY_SELECTOR sass_selector_interpolation_exp?
    ;

sass_debug
    :
    ( SASS_DEBUG | SASS_WARN ) ws cp_expression
    ;

sass_error
    :
    SASS_ERROR ws STRING
    ;

sass_control
    :
    sass_if | sass_for | sass_each | sass_while
    ;

sass_if
    :
    SASS_IF ws? sass_control_expression ws? sass_control_block (ws? sass_else)?
    ;

sass_else
    :
    SASS_ELSE ws? sass_control_block
    |
    ((SASS_ELSE ws? {tokenNameEquals("if")}? IDENT /* if */) | SASS_ELSEIF) ws? sass_control_expression ws? sass_control_block (ws? sass_else)?
    ;

sass_control_expression
    :
    cp_expression
    ;

sass_for
    :
    SASS_FOR ws cp_variable ws {tokenNameEquals("from")}? IDENT /*from*/ ws cp_math_expression ws {tokenNameEquals("to")|tokenNameEquals("through")}? IDENT /*to, through*/ ws cp_math_expression ws? sass_control_block
    ;

sass_each
    :
    SASS_EACH ws sass_each_variables ws {tokenNameEquals("in")}? IDENT /*in*/ ws (cp_expression_list (ws? COMMA)? ws?)+  sass_control_block
    ;

sass_each_variables
    :
    cp_variable ( (ws? COMMA)=> ws? COMMA ws? cp_variable )*
    ;
 
sass_while
    :
    SASS_WHILE ws sass_control_expression ws? sass_control_block
    ;

sass_control_block
    :
    LBRACE ws? declarations? RBRACE //likely not enough!
    ;

sass_function_declaration
    :
    //I assume there can be not only the return statement in the function block,
    //but so far haven't found any such example so I put the declarations rule inside
    //and added the sass_function_return into the declarations rule itself (not fully correct)
    //as the return should be allowed only from the sass function declaration
    SASS_FUNCTION ws sass_function_name ws? LPAREN ws? cp_args_list? RPAREN ws? LBRACE ws? declarations? RBRACE
    ;

sass_function_name
    :
    IDENT
    ;

sass_function_return
    :
    SASS_RETURN ws cp_expression
    ;

sass_content
    :
    SASS_CONTENT
    ;

less_import_types: 
    {tokenNameIs(new String[]{"LESS", "CSS", "REFERENCE", "INLINE", "ONCE", "MULTIPLE", "OPTIONAL"})}? IDENT
    ; catch[ RecognitionException rce] {
        reportError(rce);
        input.consume();
    }

less_when:
    {tokenNameEquals("when")}? IDENT
    ;

key_and: 
    {tokenNameEquals("and")}? IDENT
    ;

key_or:
    {tokenNameEquals("or")}? IDENT
    ;

key_only:
    {tokenNameEquals("only")}? IDENT
    ;


    
//*** END OF LESS SYNTAX ***

// ==============================================================
// LEXER
//
// The lexer follows the normative section of WWW standard as closely
// as it can. For instance, where the ANTLR lexer returns a token that
// is unambiguous for both ANTLR and lex (the standard defines tokens
// in lex notation), then the token names are equivalent.
//
// Note however that lex has a match order defined as top to bottom
// with longest match first. This results in a fairly inefficent, match,
// REJECT, match REJECT set of operations. ANTLR lexer grammars are actaully
// LL grammars (and hence LL recognizers), which means that we must
// specifically disambiguate longest matches and so on, when the lex
// like normative grammar results in ambiguities as far as ANTLR is concerned.
//
// This means that some tokens will either be combined compared to the
// normative spec, and the paresr will recognize them for what they are.
// In this case, the token will named as XXX_YYY where XXX and YYY are the
// token names used in the specification.
//
// Lex style macro names used in the spec may sometimes be used (in upper case
// version) as fragment rules in this grammar. However ANTLR fragment rules
// are not quite the same as lex macros, in that they generate actual
// methods in the recognizer class, and so may not be as effecient. In
// some cases then, the macro contents are embedded. Annotation indicate when
// this is the case.
//
// See comments in the rules for specific details.
// --------------------------------------------------------------
//
// N.B. CSS 2.1 is defined as case insensitive, but because each character
//      is allowed to be written as in escaped form we basically define each
//      character as a fragment and reuse it in all other rules.
// ==============================================================


// --------------------------------------------------------------
// Define all the fragments of the lexer. These rules neither recognize
// nor create tokens, but must be called from non-fragment rules, which
// do create tokens, using these fragments to either purely define the
// token number, or by calling them to match a certain portion of
// the token string.
//

GEN                     : '@@@';

fragment    HEXCHAR     : ('a'..'f'|'A'..'F'|'0'..'9')  ;

fragment    NONASCII    : '\u0080'..'\uFFFF'            ;   // NB: Upper bound should be \u4177777

fragment    UNICODE     : '\\' HEXCHAR
                                (HEXCHAR
                                    (HEXCHAR
                                        (HEXCHAR
                                            (HEXCHAR HEXCHAR?)?
                                        )?
                                    )?
                                )?
                                ('\r'|'\n'|'\t'|'\f'|' ')*  ;

fragment    ESCAPE      : UNICODE | '\\' ~('\r'|'\n'|'\f'|HEXCHAR)  ;

fragment    NMSTART     : '_'
                        | 'a'..'z'
                        | 'A'..'Z'
                        | NONASCII
                        | ESCAPE
                        ;

fragment    NMCHAR      : '_'
                        | 'a'..'z'
                        | 'A'..'Z'
                        | '0'..'9'
                        | '-'
                        | NONASCII
                        | ESCAPE
                        ;

fragment    NAME        : NMCHAR+   ;

fragment    URL         : ((
                              '['|'!'|'#'|'$'|'%'|'&'|'*'|'~'|'.'|':'|'/'|'?'|'='|';'|','|'+'|'@'|'|' | '{' | '}'
                            | NMCHAR
                          )
                          (
                              '['|'!'|'#'|'$'|'%'|'&'|'*'|'~'|'.'|':'|'/'|'?'|'='|';'|','|'+'|'@'|'|' | WS  | '\"' | '{' | '}'
                            | NMCHAR
                          )*)?
                        ;


// Basic Alpha characters in upper, lower and escaped form.

fragment    A   :   ('a'|'A')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'1'
                ;
fragment    B   :   ('b'|'B')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'2'
                ;
fragment    C   :   ('c'|'C')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'3'
                ;
fragment    D   :   ('d'|'D')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'4'
                ;
fragment    E   :   ('e'|'E')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'5'
                ;
fragment    F   :   ('f'|'F')
                |   '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'6'
                ;
fragment    G   :   ('g'|'G')
                |   '\\'
                        (
                              'g'
                            | 'G'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'7'
                        )
                ;
fragment    H   :   ('h'|'H')
                | '\\'
                        (
                              'h'
                            | 'H'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'8'
                        )
                ;
fragment    I   :   ('i'|'I')
                | '\\'
                        (
                              'i'
                            | 'I'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'9'
                        )
                ;
fragment    J   :   ('j'|'J')
                | '\\'
                        (
                              'j'
                            | 'J'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('A'|'a')
                        )
                ;
fragment    K   :   ('k'|'K')
                | '\\'
                        (
                              'k'
                            | 'K'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('B'|'b')
                        )
                ;
fragment    L   :   ('l'|'L')
                | '\\'
                        (
                              'l'
                            | 'L'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('C'|'c')
                        )
                ;
fragment    M   :   ('m'|'M')
                | '\\'
                        (
                              'm'
                            | 'M'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('D'|'d')
                        )
                ;
fragment    N   :   ('n'|'N')
                | '\\'
                        (
                              'n'
                            | 'N'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('E'|'e')
                        )
                ;
fragment    O   :   ('o'|'O')
                | '\\'
                        (
                              'o'
                            | 'O'
                            | ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('F'|'f')
                        )
                ;
fragment    P   :   ('p'|'P')
                | '\\'
                        (
                              'p'
                            | 'P'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('0')
                        )
                ;
fragment    Q   :   ('q'|'Q')
                | '\\'
                        (
                              'q'
                            | 'Q'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('1')
                        )
                ;
fragment    R   :   ('r'|'R')
                | '\\'
                        (
                              'r'
                            | 'R'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('2')
                        )
                ;
fragment    S   :   ('s'|'S')
                | '\\'
                        (
                              's'
                            | 'S'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('3')
                        )
                ;
fragment    T   :   ('t'|'T')
                | '\\'
                        (
                              't'
                            | 'T'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('4')
                        )
                ;
fragment    U   :   ('u'|'U')
                | '\\'
                        (
                              'u'
                            | 'U'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('5')
                        )
                ;
fragment    V   :   ('v'|'V')
                | '\\'
                        (     'v'
                            | 'V'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('6')
                        )
                ;
fragment    W   :   ('w'|'W')
                | '\\'
                        (
                              'w'
                            | 'W'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('7')
                        )
                ;
fragment    X   :   ('x'|'X')
                | '\\'
                        (
                              'x'
                            | 'X'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('8')
                        )
                ;
fragment    Y   :   ('y'|'Y')
                | '\\'
                        (
                              'y'
                            | 'Y'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('9')
                        )
                ;
fragment    Z   :   ('z'|'Z')
                | '\\'
                        (
                              'z'
                            | 'Z'
                            | ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('A'|'a')
                        )
                ;



// ---------------------
// HTML comment open.   HTML/XML comments may be placed around style sheets so that they
//                      are hidden from higher scope parsing engines such as HTML parsers.
//                      They comment open is therfore ignored by the CSS parser and we hide
//                      it from the ANLTR parser.
//
CDO             : '<!--'

                    {
                        $channel = 3;   // CDO on channel 3 in case we want it later
                    }
                ;

// ---------------------
// HTML comment close.  HTML/XML comments may be placed around style sheets so that they
//                      are hidden from higher scope parsing engines such as HTML parsers.
//                      They comment close is therfore ignored by the CSS parser and we hide
//                      it from the ANLTR parser.
//
CDC             : '-->'

                    {
                        $channel = 4;   // CDC on channel 4 in case we want it later
                    }
                ;

INCLUDES        : '~='      ;
DASHMATCH       : '|='      ;
BEGINS          : '^='      ;
ENDS            : '$='      ;
CONTAINS        : '*='      ;

GREATER         : '>'       ;
LBRACE          : '{'       ;
RBRACE          : '}'       ;
LBRACKET        : '['       ;
RBRACKET        : ']'       ;
OPEQ            : '='       ;
SEMI            : ';'       ;
COLON           : ':'       ;
DCOLON          : '::'       ;
SOLIDUS         : '/'       ;
MINUS           : '-'       ;
PLUS            : '+'       ;
STAR            : '*'       ;
LPAREN          : '('       ;
RPAREN          : ')'       ;
COMMA           : ','       ;
DOT             : '.'       ;
TILDE		: '~'       ;
PIPE            : '|'       ;
PERCENTAGE_SYMBOL
                : '%'       ;
EXCLAMATION_MARK: '!'       ;

CP_EQ           : '=='       ;
CP_NOT_EQ       : '!='       ;
LESS            : '<'       ;
GREATER_OR_EQ   : '>=' | '=>'; //a weird operator variant supported by SASS
LESS_OR_EQ      : '=<' | '<='; //a weird operator variant supported by SASS
LESS_AND        : '&' '-'*    ;
CP_DOTS         : '...';
LESS_REST       : '@rest...';

// -----------------
// Literal strings. Delimited by either ' or "
//
fragment    INVALID :;
STRING          : '\'' ( ~('\r'|'\f'|'\'') )*
                    (
                          '\''
                        | { $type = INVALID; }
                    )

                | '"'  ( ('\\\"') => '\\\"' | ('\\\\') => '\\\\' | ~ ('\r'|'\f'|'"') )*
                    (
                          '"'
                        | { $type = INVALID; }
                    )
                ;

LESS_JS_STRING  : '`' ( ~('\r'|'\f'|'`') )*
                    (
                          '`'
                        | { $type = INVALID; }
                    )
                    ;

NOT		: 'NOT';

// Variable. Used to define properties, that can be used in the grammar by the
//           var function (https://www.w3.org/TR/css-variables-1/)
VARIABLE        : '--' NMSTART NMCHAR*  ;

// -------------
// Identifier.  Identifier tokens pick up properties names and values
//
IDENT           : '-'? NMSTART NMCHAR*  ;

// -------------
// Reference.   Reference to an element in the body we are styling, such as <XXXX id="reference">
//
HASH_SYMBOL     : '#';
HASH            : HASH_SYMBOL NAME;

IMPORTANT_SYM   : EXCLAMATION_MARK (WS|COMMENT)* 'IMPORTANT'   ;

IMPORT_SYM          : '@IMPORT';
PAGE_SYM            : '@PAGE';
MEDIA_SYM           : '@MEDIA';
NAMESPACE_SYM       : '@NAMESPACE' ;
CHARSET_SYM         : '@CHARSET';
COUNTER_STYLE_SYM   : '@COUNTER-STYLE';
FONT_FACE_SYM       : '@FONT-FACE';
SUPPORTS_SYM        : '@SUPPORTS';
LAYER_SYM           : '@LAYER';
CONTAINER_SYM       : '@CONTAINER';

TOPLEFTCORNER_SYM     :'@TOP-LEFT-CORNER';
TOPLEFT_SYM           :'@TOP-LEFT';
TOPCENTER_SYM         :'@TOP-CENTER';
TOPRIGHT_SYM          :'@TOP-RIGHT';
TOPRIGHTCORNER_SYM    :'@TOP-RIGHT-CORNER';
BOTTOMLEFTCORNER_SYM  :'@BOTTOM-LEFT-CORNER';
BOTTOMLEFT_SYM        :'@BOTTOM-LEFT';
BOTTOMCENTER_SYM      :'@BOTTOM-CENTER';
BOTTOMRIGHT_SYM       :'@BOTTOM-RIGHT';
BOTTOMRIGHTCORNER_SYM :'@BOTTOM-RIGHT-CORNER';
LEFTTOP_SYM           :'@LEFT-TOP';
LEFTMIDDLE_SYM        :'@LEFT-MIDDLE';
LEFTBOTTOM_SYM        :'@LEFT-BOTTOM';
RIGHTTOP_SYM          :'@RIGHT-TOP';
RIGHTMIDDLE_SYM       :'@RIGHT-MIDDLE';
RIGHTBOTTOM_SYM       :'@RIGHT-BOTTOM';

MOZ_DOCUMENT_SYM      : '@-MOZ-DOCUMENT';
WEBKIT_KEYFRAMES_SYM  :	'@-WEBKIT-KEYFRAMES';

//this generic at rule must be after the last of the specific at rule tokens
SASS_CONTENT        : '@CONTENT';
SASS_MIXIN          : '@MIXIN';
SASS_INCLUDE        : '@INCLUDE';
SASS_EXTEND         : '@EXTEND';
SASS_DEBUG          : '@DEBUG';
SASS_ERROR          : '@ERROR';
SASS_WARN           : '@WARN';
SASS_IF             : '@IF';
SASS_ELSE           : '@ELSE';
SASS_ELSEIF         : '@ELSEIF'; //@elseif
SASS_FOR            : '@FOR';
SASS_FUNCTION       : '@FUNCTION';
SASS_RETURN         : '@RETURN';
SASS_USE            : '@USE';
SASS_FORWARD        : '@FORWARD';

SASS_EACH           : '@EACH';
SASS_WHILE          : '@WHILE';
SASS_AT_ROOT        : '@AT-ROOT';

AT_SIGN             : '@';
AT_IDENT	    : (AT_SIGN | (AT_SIGN AT_SIGN)) NMCHAR+;

SASS_VAR            : '$' NMCHAR+;
SASS_DEFAULT        : '!DEFAULT';
SASS_OPTIONAL       : '!OPTIONAL';
SASS_GLOBAL         : '!GLOBAL';

SASS_EXTEND_ONLY_SELECTOR
                    : PERCENTAGE_SYMBOL NMCHAR+;

// ---------
// Numbers. Numbers can be followed by pre-known units or unknown units
//          as well as '%' it is a precentage. Whitespace cannot be between
//          the numebr and teh unit or percent. Hence we scan any numeric, then
//          if we detect one of the lexical sequences for unit tokens, we change
//          the lexical type dynamically.
//
//          Here we first define the various tokens, then we implement the
//          number parsing rule.
//
fragment    EMS         :;  // 'em'
fragment    EXS         :;  // 'ex'
fragment    LENGTH      :;  // 'px'. 'cm', 'mm', 'in'. 'pt', 'pc'
fragment    REM		:;  // 'rem'
fragment    ANGLE       :;  // 'deg', 'rad', 'grad'
fragment    TIME        :;  // 'ms', 's'
fragment    FREQ        :;  // 'khz', 'hz'
fragment    DIMENSION   :;  // nnn'Somethingnotyetinvented'
fragment    PERCENTAGE  :;  // '%'
fragment    RESOLUTION  :;  //dpi,dpcm

NUMBER
    :   (
              '0'..'9'+ ('.' '0'..'9'+)?
            | '.' '0'..'9'+
        )
        (
              (D P (I|C))=>
                D P
                (
                     I | C M
                )
                { $type = RESOLUTION; }

            | (E (M|X))=>
                E
                (
                      M     { $type = EMS;          }
                    | X     { $type = EXS;          }
                )
            | (P(X|T|C))=>
                P
                (
                      X
                    | T
                    | C
                )
                            { $type = LENGTH;       }
            | (C M)=>
                C M         { $type = LENGTH;       }
            | (M (M|S))=>
                M
                (
                      M     { $type = LENGTH;       }

                    | S     { $type = TIME;         }
                )
            | (I N)=>
                I N         { $type = LENGTH;       }

            | (D E G)=>
                D E G       { $type = ANGLE;        }
//            | (R A D)=>
//                R A D       { $type = ANGLE;        }

            | (R (A|E))=>
                R
                (
                   A D       {$type = ANGLE;         }
                 | E M       {$type = REM;           }
                )

            | (S)=>S        { $type = TIME;         }

            | (K? H Z)=>
                K? H    Z   { $type = FREQ;         }

            | IDENT         { $type = DIMENSION;    }

            | PERCENTAGE_SYMBOL { $type = PERCENTAGE;   }

            | // Just a number
        )
    ;

// ------------
// url and uri.
//
URI :   U R L
        '('
            ((WS)=>WS)? (URL|STRING) WS?
        ')'
    ;

fragment HEXCHAR_WILDCARD: '?' | HEXCHAR;

URANGE: ('u'|'U') PLUS HEXCHAR_WILDCARD+ (MINUS HEXCHAR_WILDCARD+)?;

MOZ_URL_PREFIX
	:
	'URL-PREFIX('
            ((WS)=>WS)? (URL|STRING) WS?
        ')'

    	;

MOZ_DOMAIN
	:
	'DOMAIN('
            ((WS)=>WS)? (URL|STRING) WS?
        ')'

    	;

MOZ_REGEXP
	:
	'REGEXP('
            ((WS)=>WS)? STRING WS?
        ')'

        	;

// -------------
// Whitespace.  Though the W3 standard shows a Yacc/Lex style parser and lexer
//              that process the whitespace within the parser, ANTLR does not
//              need to deal with the whitespace directly in the parser.
//
WS
    :
    (' '|'\t')+
    ;

NL
    :
//    ('\r' '\n'? | '\n')
    ('\r' | '\n')+
    ;

// Comments.    Comments may not be nested, may be multilined and are delimited
//              like C comments: /* ..... */
COMMENT
    :
    '/*' ( options { greedy=false; } : .*) '*/'
    ;

LINE_COMMENT
    :
    '//'( options { greedy=false; } : ~('\r' | '\n')* ) {
	if (isCssPreprocessorSource()) {$channel = HIDDEN;}
    }
    ;

// -------------
//  Illegal.    Any other character shoudl not be allowed.
//
